#include "utilc_def.h"
#include "utilc_linked_list.h"

util_linked_list_node_t* util_linked_list_node_create(void *value, DESTORY_FUN destory_fun) {
    SAFE_MALLOC(util_linked_list_node_t, ret);
    ret->value = value;
    ret->destory_fun = destory_fun;
    return ret;
}

void util_linked_list_node_ref(util_linked_list_node_t* nod){
    nod->ref++;
}

void util_linked_list_node_unref(util_linked_list_node_t* nod){
    if(!nod->ref)
        nod->destory_fun(nod);
    else
        nod->ref--;
}

util_linked_list_t* util_linked_list_create(){
    SAFE_MALLOC(util_linked_list_t, ret);
    return ret;
}

void util_linked_list_clear(util_linked_list_t *lst){
    util_linked_list_node_t *tmp = NULL;
    if(lst->size==0)
        return;
    tmp = lst->head;
    while(tmp)
        util_linked_list_node_unref(tmp);
    lst->head = NULL;
    lst->tail = NULL;
    lst->size = 0;
}

void util_linked_list_destory(util_linked_list_t *lst){
    util_linked_list_clear(lst);
    free(lst);
};

void util_linked_list_ref(util_linked_list_t *lst){
    lst->ref++;
}

void util_linked_list_unref(util_linked_list_t *lst){
    if(lst->ref == 0)
        util_linked_list_destory(lst);
    else
        lst->ref--;
}

void util_linked_list_push_back(util_linked_list_t *lst, util_linked_list_node_t *val){
    util_linked_list_node_ref(val);
    if(lst->size == 0) {
        lst->head = val;
        lst->tail = val;
        lst->size = 1;
        return;
    }
    lst->tail->next = val;
    lst->tail = val;
    lst->size++;
}

void util_linked_list_push_front(util_linked_list_t *lst, util_linked_list_node_t *val){
    util_linked_list_node_ref(val);
    if(lst->size == 0) {
        lst->head = val;
        lst->tail = val;
        lst->size = 1;
        return;
    }
    val->next = lst->head;
    lst->head = val;
    lst->size++;
}

util_linked_list_node_t* util_linked_list_front(util_linked_list_t *lst){
    return lst->head;
}

util_linked_list_node_t* util_linked_list_back(util_linked_list_t *lst){
    return lst->tail;
}

void util_linked_list_pop_front(util_linked_list_t *lst){
    util_linked_list_node_t *tmp = lst->head;
    lst->head = lst->head->next;
    util_linked_list_node_unref(tmp);
    if(lst->head == NULL) lst->tail = NULL;
    lst->size--;
}

void util_linked_list_pop_back(util_linked_list_t *lst){
    util_linked_list_node_t *tmp = NULL;
    if(lst->size==0) return;
    if(lst->size==1) {
        util_linked_list_node_unref(lst->head);
        lst->head = NULL;
        lst->tail = NULL;
        lst->size = 0;
        return;
    }
    tmp = lst->head;
    for(;tmp->next != lst->tail; tmp=tmp->next);
    util_linked_list_node_unref(lst->tail);
    tmp->next=NULL;
    lst->tail=tmp;
    lst->size--;
}

void util_linked_list_insert(util_linked_list_t *lst, util_linked_list_node_t *pos, util_linked_list_node_t *val){
    util_linked_list_node_ref(val);
    val->next = pos->next;
    pos->next = val;
    lst->size++;
}

void util_linked_list_remove(util_linked_list_t *lst, util_linked_list_node_t *val){
    util_linked_list_node_t *tmp = lst->head;
    if(lst->size==0) return;
    if(lst->size==1) {
        if(tmp == val) {
            lst->head = NULL;
            lst->tail = NULL;
            lst->size = 0;
            util_linked_list_node_unref(val);
            return;
        }
    }
    if(tmp == val) {
        lst->head = lst->head->next;
        lst->size--;
        util_linked_list_node_unref(val);
        return;
    }
    for(;tmp->next != lst->tail; tmp=tmp->next);
    if(tmp==NULL) return;
    tmp->next = val->next;
    if(tmp->next==NULL) lst->tail = tmp;
    lst->size--;
    util_linked_list_node_unref(val);
}

uint32_t util_linked_list_size(util_linked_list_t *lst) {
    return lst->size;
}